/* Date.java */

import java.io.*;

class Date {

    /* Put your private data fields here. */
    private int day = -1;
    private int month = -1;
    private int year = -1;
    
   

    /**
     * Constructs a date with the given month, day and year. If the date is not
     * valid, the entire program will halt with an error message.
     *
     * @param month is a month, numbered in the range 1...12.
     * @param day is between 1 and the number of days in the given month.
     * @param year is the year in question, with no digits omitted.
     */
    public Date(int month, int day, int year) {

        if (Date.isValidDate(month, day, year)) {
            this.day = day;
            this.month = month;
            this.year = year;

        } else {
            System.err.println("Date: Invalid Date.");
            System.exit(0);
        }

    }

    /**
     * Constructs a Date object corresponding to the given string.
     *
     * @param s should be a string of the form "month/day/year" where month must
     * be one or two digits, day must be one or two digits, and year must be
     * between 1 and 4 digits. If s does not match these requirements or is not
     * a valid date, the program halts with an error message.
     */
    public Date(String s) {
        if (s != null) {

            String[] tokens = s.split("/");

            if (tokens.length == 3) {

                try {
                    
                    int m = Integer.parseInt(tokens[0]);
                    int d = Integer.parseInt(tokens[1]);
                    int y = Integer.parseInt(tokens[2]);
                    
                    if(Date.isValidDate(m, d, y))
                    {
                        this.month = m;
                        this.day = d;
                        this.year = y;
                        return;
                    }else{
                         System.err.println("Date: Invalid Date.");
                            System.exit(0);
                    }
                    
                    
                    
                } catch (Exception e) {
                    // just let the code continue to the error 
                    // message at the bottom of the method. 
                }

            }
        }
        System.err.println("Date: Invalid Date.");
        System.exit(0);

    }

    /**
     * Checks whether the given year is a leap year.
     *
     * @return true if and only if the input year is a leap year.
     */
    public static boolean isLeapYear(int year) {
        /*
        
         A leap year is any year divisible by 4, except that a year divisible by 100 is not a
         leap year, except that a year divisible by 400 is a leap year after all.
        
         Hence, 1800 and 1900 are not leap years, but 1600 and 2000 are.  (Implement
         this rule in your program even if you know information to the contrary.)
      
         */

        if (year % 400 == 0) {
            return true;
        } else if (year % 100 == 0) {
            return false;
        } else if (year % 4 == 0) {
            return true;
        } else // the everything else. 
        {
            return false;
        }

    }

    /**
     * Returns the number of days in a given month.
     *
     * @param month is a month, numbered in the range 1...12.
     * @param year is the year in question, with no digits omitted.
     * @return the number of days in the given month.
     */
    public static int daysInMonth(int month, int year) {
        int numDays = -1;

        /*
         The Unix "cal" command will remind you of the number of days in each month.
         February contains 28 days most years, but 29 days during a leap year.  
      
         30 Days have September
         April, June, and November:
         All the rest have 31

         Except Februrary,
         which has 28 days clear
         and 29 in a Leap Year.
         */
        switch (month) {

            case 4:
            // April
            case 6:
            // June
            case 9:
            //September
            case 11:
                // November
                numDays = 30;
                break;
            case 2:
                //Februrary
                if (isLeapYear(year)) {
                    numDays = 29;
                } else {
                    numDays = 28;
                }
                break;
            default:
                numDays = 31;
                break;
        }

        return numDays;                           // replace this line with your solution
    }

    /**
     * Checks whether the given date is valid.
     *
     * @param month is a month, numbered in the range 1...12.
     * @param year is the year in question, with no digits omitted.
     * @return true if and only if month/day/year constitute a valid date.
     *
     * Years prior to A.D. 1 are NOT valid.
     */
    public static boolean isValidDate(int month, int day, int year) {
        // Check to see if the number of days in the month is a possibility
        if ((day <= daysInMonth(month, year)) && (day > 0)) {
            // check the months
            if ((month > 0) && (month < 13)) {
                // check the year
                if (year >= 1) {
                    return true;
                }

            }
        }

        // one of the checks failed, return false.
        return false;
    }

    /**
     * Returns a string representation of this date in the form month/day/year.
     * The month, day, and year are expressed in full as integers; for example,
     * 12/7/2006 or 3/21/407.
     *
     * @return a String representation of this date.
     */
    public String toString() {
        return month + "/" + day + "/" + year;                     // replace this line with your solution
    }

    /**
     * Determines whether this Date is before the Date d.
     *
     * @return true if and only if this Date is before d.
     */
    public boolean isBefore(Date d) {
        // note, you can have the same year and still be before. 
        if (d.year >= this.year) {

            // Check to see if it is equal, if it is, then inspect it further. 
            if (d.year == this.year) {
                // note, you can have the same month, yet still have a date before. 

                if (d.month >= this.month) {

                    // we need to check for great specificity
                    if (d.month == this.month) {
                        if (d.day > this.day) {
                            return true;
                        }
                    } else {
                        // the month incoming month is less, return true. 
                        return true;
                    }
                }
            } else {

                // the incoming year is smaller than this one, return true. 
                return true;
            }
        }

        // some checks were failed. 
        return false;

    }

    /**
     * Determines whether this Date is after the Date d.
     *
     * @return true if and only if this Date is after d.
     */
    public boolean isAfter(Date d) {

        // note, you can have the same year and still be before. 
        if (d.year <= this.year) {

            // Check to see if it is equal, if it is, then inspect it further. 
            if (d.year == this.year) {
                // note, you can have the same month, yet still have a date before. 

                if (d.month <= this.month) {

                    // we need to check for great specificity
                    if (d.month == this.month) {
                        if (d.day < this.day) {
                            return true;
                        }
                    } else {
                        // the month incoming month is less, return true. 
                        return true;
                    }
                }
            } else {

                // the incoming year is smaller than this one, return true. 
                return true;
            }
        }

        // some checks were failed. 
        return false;                    // replace this line with your solution
    }

    /**
     * Returns the number of this Date in the year.
     *
     * @return a number n in the range 1...366, inclusive, such that this Date
     * is the nth day of its year. (366 is used only for December 31 in a leap
     * year.)
     */
    public int dayInYear() {
        int totalDays = 0;
        
        for (int mon=1; mon<this.month; mon++)
        {
            totalDays += daysInMonth(mon, this.year);
        }
        
        // then add the number of days we have listed, in this month.
        totalDays += this.day;
        
        return totalDays;                           
    }
    
    /**
     * Responsible for figuring out how many days have elapsed since the specified date from 1 AD. 
     * @param d The date you are trying to figure out how many days have occurred. 
     * @return The total number of days that have elapsed since 1 AD. 
     */
    private int calcTotalDaysSinceOneAd(Date d)
    {
        int totalDaysSince1Ad = 0;
        
        // Add the current day # to the total. 
        totalDaysSince1Ad += d.day;
        int otherNumMon = d.month -1; // subtract one from the month so we don't count this month twice.
        
        // count down from the current year and month to 1AD
        for (int year=d.year; year>=1; year--)
        {
            // find out what limit we need
            for (int mon=otherNumMon; mon>=1; mon--)
            {
                totalDaysSince1Ad += daysInMonth(mon, year);
            }
            
            // set the num of months to 12 once we move past the initial date.
            otherNumMon = 12;  
        }
        
        return totalDaysSince1Ad;
    }

    /**
     * Determines the difference in days between d and this Date. For example,
     * if this Date is 12/15/2012 and d is 12/14/2012, the difference is 1. If
     * this Date occurs before d, the result is negative.
     *
     * @return the difference in days between d and this date.
     */
    public int difference(Date other) {
        int delta = -1;
        int otherTotalDays = calcTotalDaysSinceOneAd(other);
        int thisTotalDays  = calcTotalDaysSinceOneAd(this);
        
        delta =  thisTotalDays - otherTotalDays;
        
        return delta;                           // replace this line with your solution
    }

    public static void main(String[] argv) {

        System.out.println("\nTesting helper methods.");
        Date leapYearFeb400 = new Date(2, 29, 400);

        System.out.println("\t Dave valid (2/29/401): " + Date.isValidDate(2, 28, 401));
        System.out.println("\t Dave not valid (2, 29, 400): " + Date.isValidDate(2, 29, 401));
        System.out.println("\t Dave not valid (2, 29, 100): " + Date.isValidDate(2, 29, 401));

        System.out.println("\nTesting constructors.");
        Date d1 = new Date(1, 1, 1);
        System.out.println("Date should be 1/1/1: " + d1);
        d1 = new Date("2/4/2");
        System.out.println("Date should be 2/4/2: " + d1);
        d1 = new Date("2/29/2000");
        System.out.println("Date should be 2/29/2000: " + d1);
        d1 = new Date("2/29/1904");
        System.out.println("Date should be 2/29/1904: " + d1);

        d1 = new Date(12, 31, 1975);
        System.out.println("Date should be 12/31/1975: " + d1);
        Date d2 = new Date("1/1/1976");
        System.out.println("Date should be 1/1/1976: " + d2);
        Date d3 = new Date("1/2/1976");
        System.out.println("Date should be 1/2/1976: " + d3);

        Date d4 = new Date("2/27/1977");
        Date d5 = new Date("8/31/2110");
        
        Date yearOne = new Date("1/1/2");
        Date yearTwo = new Date("12/31/1");

        /* I recommend you write code to test the isLeapYear function! */
        System.out.println("\nTesting before and after.");
        System.out.println(d2 + " after " + d1 + " should be true: "
                + d2.isAfter(d1));
        System.out.println(d3 + " after " + d2 + " should be true: "
                + d3.isAfter(d2));
        System.out.println(d1 + " after " + d1 + " should be false: "
                + d1.isAfter(d1));
        System.out.println(d1 + " after " + d2 + " should be false: "
                + d1.isAfter(d2));
        System.out.println(d2 + " after " + d3 + " should be false: "
                + d2.isAfter(d3));

        System.out.println(d1 + " before " + d2 + " should be true: "
                + d1.isBefore(d2));
        System.out.println(d2 + " before " + d3 + " should be true: "
                + d2.isBefore(d3));
        System.out.println(d1 + " before " + d1 + " should be false: "
                + d1.isBefore(d1));
        System.out.println(d2 + " before " + d1 + " should be false: "
                + d2.isBefore(d1));
        System.out.println(d3 + " before " + d2 + " should be false: "
                + d3.isBefore(d2));

        System.out.println("\nTesting difference.");
        System.out.println(d1 + " - " + d1 + " should be 0: "
                + d1.difference(d1));
        System.out.println(d2 + " - " + d1 + " should be 1: "
                + d2.difference(d1));
        System.out.println(d3 + " - " + d1 + " should be 2: "
                + d3.difference(d1));
        System.out.println(d3 + " - " + d4 + " should be -422: "
                + d3.difference(d4));
        System.out.println(d5 + " - " + d4 + " should be 48762: "
                + d5.difference(d4));
        
        System.out.println(yearOne + " - " + yearTwo + " should be 1: "
                + yearOne.difference(yearTwo));
    }
}
